package 
{
	import com.adobe.utils.AGALMiniAssembler;
	
	import flash.display.Bitmap;
	import flash.display.Sprite;
	import flash.display.Stage3D;
	import flash.display3D.Context3D;
	import flash.display3D.Context3DProgramType;
	import flash.display3D.Context3DTextureFormat;
	import flash.display3D.Context3DTriangleFace;
	import flash.display3D.Context3DVertexBufferFormat;
	import flash.display3D.IndexBuffer3D;
	import flash.display3D.Program3D;
	import flash.display3D.VertexBuffer3D;
	import flash.display3D.textures.Texture;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Matrix3D;
	import flash.geom.Rectangle;
	import flash.geom.Vector3D;
	
	public class MoleHillTest extends Sprite
	{
		private const WIDTH:int  = 600;
		private const HEIGHT:int = 600;
		//context3D 用来渲染，类似于bitmapdata 赋值给bitmap
		private var context3d:Context3D;
		private var vertexBuffer:VertexBuffer3D;
		private var indexBuffer:IndexBuffer3D;
		private var program:Program3D;
		private var texture:Texture;
		
		[Embed( source = "assets/123.jpg" )]
		private static var PIC:Class;
		
		//这个矩阵被用来修改顶点数据中顶点着色单元的位置（不要担心你可以很容易获得）
		private var model:Matrix3D=new Matrix3D();
		
		public function MoleHillTest()
		{
			addEventListener(Event.ADDED_TO_STAGE, init);
		}
		private function init(e:Event):void{
			this.stage.stage3Ds[0].viewPort = new Rectangle(0,0,WIDTH,HEIGHT);
			this.stage.stage3Ds[0].addEventListener(Event.CONTEXT3D_CREATE,setUp);
			this.stage.stage3Ds[0].requestContext3D();
		}
		private function setUp(e:Event):void
		{
			//获得stage3D对象
			var stage3D:Stage3D = Stage3D(e.currentTarget);
			//获得context3D
			context3d = stage3D.context3D;
			
			//如果未创建成功，直接返回，空执行无意义
			if(context3d==null)
				return;
			trace(context3d.driverInfo)
			//这个可以在debug中获得报错信息，但是在发布时需要将它设置为false
			//不然会很影响性能
			context3d.enableErrorChecking=true;
			
			//这个方法几乎与BitmapData(w,h)功能类似，我们需要显示效果更好
			//可以设置为4，4的作用是可以反锯齿（去掉锯齿）
			//还有几种设置值
			//0 表示为去锯齿
			//2 表示弱锯齿
			//4 表示高质量的去锯齿
			//16 表示最高质量的去锯齿
			//但是当我设置为16的获得了一个错误，所以可能设置为16不能工作
			context3d.configureBackBuffer(WIDTH,HEIGHT,4,true);
			
			//这里我们经常的当三角形我们看不到时
			//是否需要将它画出来
			context3d.setCulling(Context3DTriangleFace.BACK);
			
			//创建顶点缓冲
			//我们将创建3个顶点
			//每个顶点包括了6个元素（x,y,z,r,g,b）
			vertexBuffer=context3d.createVertexBuffer(8, 6);
			
			//为每个顶点创建索引缓冲（按照顺序显示三角形）
			//我们创建一个三角形只需要3个顶点，因此只有3个索引缓冲
			indexBuffer=context3d.createIndexBuffer(36);
			
			
			//我们需要创建一个顶点数据
			//每个顶点数据将包括位置和颜色
			//下面说明下数据
			//x==-1 表示“屏幕左边”
			//x==1 表示“屏幕右边”
			//x==0 表示“屏幕中间”
			//Y轴也是相同的道理
			//颜色值被表示为小数，所以
			//1，0，0 表示red 255（r,g,b）
			//0.5,0,0 表示半红 128；
			//下面的代码我将定义一个三角形：
			//左下角显示红色，上面中心叫显示绿色，右下角显示蓝色
			var vertexData:Vector.<Number>=Vector.<Number>(
				[
					-1, 1, 0,  255/255, 0, 0, //<- 第一个顶点 x,y,z,r,g,b
					1, 1, 0,    0, 255/255, 0, //<- 2nd vertex x,y,z,r,g,b
					1, -1, 0,   0, 0, 255/255 ,//<- 3rd vertex x,y,z,r,g,b
					-1, -1, 0,  255/255, 0, 255/255,
					-1, -1, 2,  0,0, 255/255,
					1, -1, 2,  255/255, 0, 0,
					1, 1, 2,  0, 255/255, 0,
					-1, 1, 2,  255/255, 255/255, 255/255
				]
			);
			
			//下面是一个索引数据，上面的顶点将会照着索引画
			//0 == 第一个顶点
			//1 == 第二个顶点
			//2 == 第三个顶点
			var indexData:Vector.<uint> = Vector.<uint>(
				[0,1,2,
				2,3,0,
				0,3,4,
				4,7,0,
				7,4,5,
				5,6,7,
				6,5,2,
				2,1,6,
				7,6,1,
				1,0,7,
				3,2,5,
				5,4,3]);
			
			//把我们的顶点数据添加到顶点缓冲中
			vertexBuffer.uploadFromVector(vertexData, 0, 8);
			
			//把我们的索引数据添加到索引缓冲中
			indexBuffer.uploadFromVector(indexData, 0, 36);
			
			
			var bitmap:Bitmap = new PIC();
			texture = context3d.createTexture(256, 256, Context3DTextureFormat.BGRA,true);
			texture.uploadFromBitmapData(bitmap.bitmapData); 
			
			//我们需要AGAL来编译我们的顶点和其他的片段（以哥的理解是像素着色
			//主要是用AGAL来将我们的数据翻译为字节码然后送给显卡处理） AGALMiniAssembler
			//这个类是adobe为了方便处理提供的
			var agalVertex:AGALMiniAssembler=new AGALMiniAssembler();
			var agalFragment:AGALMiniAssembler=new AGALMiniAssembler();
			
			//我们开始做AGAL编码
			//AGAL 是 非常“基础的”
			//他可以构成各种执行程序
			//每个执行程序都至少包括
			//操作符 OutPut， Input1
			//有时候你可以用2个input
			//操作符 OutPut，Input1，Input2
			
			//他们该怎么工作呢，我们想知道一些类似
			//I*J==K
			//我们AGAL执行程序像下面这样
			//mul K, I, J
			
			//mul==操作符
			//K==Output
			//I==Input1
			//J==Input2
			//这里是一些下面你会看到的一些定义
			//m44==操作运行你的数据矩阵
			//op == 输出你定义的顶点数据转化屏幕坐标
			//va0==我们三角形顶点数据中的x,y,z
			//vc0==将会被定义的常量(矩阵中用来更改顶点)
			//v0== 一个"变量" 位于顶点着色器和片段着色其之间
			//va1==顶点中的r,g,b颜色
			//这些资源代码提供给顶点着色器AGAL程序处理
			
			//这里讲解下下面几行代码用来做那些事情
			//m44 op，va0，vc0 提交一个4×4的矩阵转化到屏幕顶点并且输出
			//mov v0，va1 从顶点中取出颜色输送到片段着色器v0
			var agalVertexSource:String="m44 op, va0, vc0\n" +
				"mov v0, va1\n";
			/*var agalVertexSource:String="dp4 op.x, va0, vc0\n" +
			"dp4 op.y, va0, vc1\n" +
			"dp4 op.z, va0, vc2\n" +
			"dp4 op.w, va0, vc3\n" +
			"mov v0, va1.xyzw\n";*/
			
			//oc == 输出颜色（屏幕上的颜色）
			//v0 == 位于顶点着色器和片段着色器中的变量
			//mov oc，v0 取出我们的颜色输出到屏幕
			//var agalFragmentSource:String="mov oc, v0\n";
			var agalFragmentSource:String="mov ft0, v0\n" +
											"tex ft1, ft0, fs1 <2d,clamp,linear>\n" +
											"mov oc, ft1\n";
			
			//编译我们的AGAL代码
			//当我们编译好我们可以得到一个我们着色其可以使用的byteArray字节数组，
			agalVertex.assemble(Context3DProgramType.VERTEX, agalVertexSource);
			agalFragment.assemble(Context3DProgramType.FRAGMENT, agalFragmentSource);
			

			//创建一个Program，这样我们可以利用显卡着色器
			//记住我们的这个program包括了一个顶点着色器和一个片段着色器
			program=context3d.createProgram();
			
			//将编译好的像素着色单元传送给Program使用
			//agalcode==bytearray
			program.upload(agalVertex.agalcode, agalFragment.agalcode);
			
			
			
			//增加一个渲染循环函数
			//addEventListener(Event.ENTER_FRAME,frame);
			stage.addEventListener(MouseEvent.MOUSE_MOVE,frame);
		}
		private function frame(e:MouseEvent):void
		{
			//清除屏幕
			context3d.clear(0.5, 0.5, 0.5, 1);
			
			//设置program来渲染到屏幕
			context3d.setProgram(program);
			
			//这里很奇怪吗？为什么要写2次？
			//基本上因为我们的AGAL代码中的va0将会变成我们定义的x，y，z
			//va1会变成我们定义的颜色r，g，b
			//0 <-- va0 (出自上面的定义mov v0, va1\n)
			//顶点缓冲 <-- 顶点缓冲中的坐标
			//第二个0 <--程序需要查找第开始点赋值给va0（因为x，y，z的位置起始点是0）
			//Context3DVertexBufferFormat.FLOAT_3 <- 自动生成3个变量用来定义x，y，z
			
			//1 <-- va1
			//顶点缓冲的颜色
			//3<-- 颜色是从哪里开始的用来赋值va1（因为r，g，b是从3这个位置为起始点）
			//Context3DVertexBufferFormat.FLOAT_3 <- 自动生成3个变量用来定义r，g，b
			//I think you'd use Context3DVertexBufferFormat.FLOAT_4 if you were using r,g,b,a
			
			
			//重置矩阵
			model.identity();

			//缩小三角形到原来的1倍（就是x，y，z都缩小）
			
			model.appendRotation(mouseX, Vector3D.X_AXIS); 
			model.appendRotation(mouseX+mouseY, Vector3D.Z_AXIS);
			model.appendRotation(mouseY, Vector3D.Y_AXIS);
			
			model.appendScale(0.3, 0.3, 0.3);
			
			//这里vc0就是我们定义的modex矩阵
			//这样将会影响我们的顶点数据
			context3d.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, model);
			

			//将三角形画在屏幕上
			//0 代表我们从哪个索引开始画
			//1 代表我们要画几个三角形
			
			context3d.setVertexBufferAt(0, vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_3);
			context3d.setVertexBufferAt(1, vertexBuffer, 3, Context3DVertexBufferFormat.FLOAT_3);
			context3d.setTextureAt(1,texture);
			context3d.drawTriangles(indexBuffer, 0, 2);
			
			//显示三角形到屏幕上
			context3d.present();
		}
	}
}